﻿using LinkGame.Models.Variations;

namespace LinkGame.Models
{
    internal sealed class GameLevel : IDisposable
    {
        #region Private Fields

        private readonly Map _map;

        #endregion Private Fields

        #region Public Constructors

        public GameLevel(string name, int width, int height, int numOfTileTypes, IVariation variation)
        {
            _map = new(width, height, numOfTileTypes);
            _map.TileChanged += OnMapTileChanged;
            Variation = variation;
            Name = name;
            RefreshMap();
        }

        #endregion Public Constructors

        #region Public Events

        public event EventHandler<LevelResolvedEventArgs>? LevelResolved;

        public event EventHandler<TileChangedEventArgs>? MapTileChanged;

        public event EventHandler<PathResolvedEventArgs>? PathResolved;

        #endregion Public Events

        #region Public Properties

        public IVariation Variation { get; }

        public string Name { get; }

        #endregion Public Properties

        #region Public Methods

        public void Dispose()
        {
            _map.TileChanged -= OnMapTileChanged;
        }

        public int GetTileValue(int x, int y) => _map.GetTileValue(x, y);

        public bool HasValidConnection(Point p1, Point p2)
        {
            var result = _map.TryResolve(p1, p2, out var resolutionPath);
            if (!result) return false;

            PathResolved?.Invoke(this, new(resolutionPath));

            _map.SetTileValue(p1.X, p1.Y);
            _map.SetTileValue(p2.X, p2.Y);
            _map.ApplyVariation(Variation, p1, p2);
            if (_map.Resolved)
            {
                LevelResolved?.Invoke(this, new(this));
            }
            else
            {
                RefreshMap();
            }

            return true;
        }

        public IEnumerable<ResolutionPath> PossibleResolutions => _map.GetPossibleResolutions();

        private void OnMapTileChanged(object? sender, TileChangedEventArgs e)
        {
            MapTileChanged?.Invoke(_map, e);
        }

        private void RefreshMap()
        {
            var hasResolution = _map.HasResolution();
            while (!hasResolution)
            {
                _map.Refresh();
                hasResolution = _map.HasResolution();
            }
        }

        #endregion Private Methods
    }
}